#ifndef F_CPU
#define F_CPU           16000000UL  // 16 MHz
#endif


#include                <stdlib.h>
#include                <avr/io.h>
#include                <avr/interrupt.h>
#include                <util/delay.h>
#include                <WProgram.h>
#include                <DS1307.h>
#include                <Wire.h>
#include                <stdio.h>
#include                <EEPROM.h>

//EEPROM Adress to write clockmode to
int ClockmodeAddr = 1;

// The number of "pie slices" that make up a single image around the platter
#define DIVISIONS       255

// Red pin, on PORTD
#define RED             3
// Green Pin, on PORTD
#define GRN             7
// Blue pin, on PORTD
#define BLU             6


#define modePin 8

#define motorPin 9

#define minutePin 11

#define hourPin 12



// Macro used to communicate serial status
#define OK              1

// Number of pages in frame buffer
#define PAGES           2

// Helper macro to build LED values for PORTD
#define RGB(R,G,B)      (R << RED | G << GRN | B << BLU)

// Helper macros for frobbing bits
#define bitset(var,bitno) ((var) |= (1 << (bitno)))
#define bitclr(var,bitno) ((var) &= ~(1 << (bitno)))
#define bittst(var,bitno) (var& (1 << (bitno)))

// It is technically possible to change the number of divisions, even
// though this is not currently implemented.  This const serves as a place
// holder for future enhancements.
const unsigned char divisions = DIVISIONS;

// The current slice being drawn
volatile unsigned char current_slice;

// The period of the platter in timer ticks
int period;

// The value of the hidden page
volatile int page_hidden;

// The value of the visible page
volatile int page_visible;

// A flag representing the need for the pages to be flipped
volatile unsigned char page_flip_flag;

// The double buffered frame buffer
unsigned char FrameBuffer[PAGES][DIVISIONS];

int second;
int minute;
int hour;

int DisplaySecond;
int DisplayMinute;
int DisplayHour;

//Read clockmode from EEPROM Memory
byte modePushCounter = EEPROM.read(ClockmodeAddr)-1;

int modeState;
int lastmodeState;

/**
 ** FrameBuffer / Page routines
 ** 
 ** The framebuffer is double buffered, which allows you to write updates
 ** to the "hidden" page while the code draws from the "visible" page.
 **
 ** page_flip() makes the hidden page the visible page, and vice-versa.
 **
 ** copy_page() allows you to copy the visible page back to the hidden page, 
 ** so you don't have to redraw the entire frame if you are just making a 
 ** small change.  
 **
 ** clear_page() clears the hidden page.
 **
 ** write_page() writes to the hidden page.
 ** 
 ** See RunStartupDisplay() for a simple example.
 **/
void setTime() {

  static boolean hourPressed=false, minPressed=false; //track button state


    if (digitalRead (minutePin)==LOW && minPressed == false) {// if a normally closed switch is pressed

    if (minute == 59) minute = -1; // sets minute to -1 if minutes = 59 to begin at 0 again

    minute++; 
    RTC.stop();
    RTC.set(DS1307_SEC,0);        
    RTC.set(DS1307_MIN,minute);   
    RTC.set(DS1307_HR,hour);    
    RTC.start();
    minPressed = true;            // note the pressed state
  }

  if (digitalRead (minutePin)==HIGH) minPressed = false; // reset the state when the button is released

  if (digitalRead (hourPin)==LOW && hourPressed == false) {

    if (hour == 23) hour = -1; // sets hour to -1 if hour > 23 to begin at 0 again
    hour++; 
    RTC.stop();
    RTC.set(DS1307_SEC,0);        
    RTC.set(DS1307_MIN,minute);   
    RTC.set(DS1307_HR,hour);      
    RTC.start();  

    hourPressed = true;          // note the pressed state
  }


  if (digitalRead (hourPin)==HIGH) hourPressed = false;

}

// sit and wait for the page to be flipped
void __inline__ wait_for_page_flip(void)
{
  while (page_flip_flag) {
  }
}

// request a page flip, and wait for the sensor interrupt to flip the page
void __inline__ flip_page(void)
{
  page_flip_flag = 1;
  wait_for_page_flip();
}

// copy the visible page to the hidden page
void __inline__ copy_page(void)
{
  int x;
  for(x = 0; x < divisions; x++)
  {
    FrameBuffer[page_hidden][x] = FrameBuffer[page_visible][x];
  }
}

// write a value to the hidden page
void __inline__ write_page(unsigned char slice, unsigned char val)
{
  FrameBuffer[page_hidden][slice] = val;
}

// clear the hidden page
void clear_page(void)
{
  int x;
  for(x = 0; x < divisions; x += 1)
  {
    FrameBuffer[page_hidden][x] = 0;
  }
}

// called by the interrupt to flip to the next page
void __inline__ flip_to_next_page(void)
{
  if (page_flip_flag)
  {
    page_visible = page_hidden;
    page_hidden = !page_hidden;
    page_flip_flag = 0;
  }
}

// Go through all pages of the frame buffer, and set them to 0
void init_pages(void)
{
  int page;
  int x;
  for(page = 0; page < PAGES; page += 1)
  {
    for(x = 0; x < divisions; x += 1)
    {
      FrameBuffer[page][x] = 0;
    }
  }
  page_hidden = 0;
  page_flip_flag = 0;
}

// Configure the Serial Port, LED Pins, Timers, and Hardware Interrupt
void SetupHardware(void)
{


  pinMode(RED, OUTPUT);
  pinMode(GRN, OUTPUT);
  pinMode(BLU, OUTPUT);
  pinMode(motorPin, OUTPUT);

  pinMode(hourPin, INPUT);
  pinMode(minutePin, INPUT);  
  pinMode(modePin, INPUT); 


  digitalWrite(minutePin, HIGH); //High turns on pull-up resistors
  digitalWrite(hourPin, HIGH);
  digitalWrite(modePin, HIGH);
  
  digitalWrite(motorPin,HIGH);


  // disable global interrupts
  cli();

  // setup timer0 - 8bit
  // resonsible for timing the LEDs
  TCCR0A = 0;
  TCCR0B = 0;  
  // select CTC mode
  bitset(TCCR0A, WGM01);
  // select prescaler clk / 8
  bitset(TCCR0B, CS01);
   
  // enable compare interrupt
  bitset(TIMSK0, OCIE0A);

  // setup timer1 - 16bit
  // responsible for timing the rotation of the platter
  TCCR1B = 0;
  TCCR1A = 0;
  // select prescaler clk / 8
  bitset(TCCR1B, CS11);
 
  // reset timer
  TCNT1 = 0;
  // enable overflow interrupt
  bitset(TIMSK1, TOIE1);


  // configure the platter interrupt PIN
  // int0, on falling
  EICRA = _BV(ISC01);
  // Enable the hardware interrupt.
  EIMSK |= _BV(INT0);

  // set the rotational period to 0
  period = 0;

  // init pages
  init_pages();

  // enable global interrupts
  sei();




}



void setup(void)
{

  SetupHardware();

}



void loop(void)
{



  //Clock mode change
  modeState = digitalRead(modePin);

  if (modeState != lastmodeState) {

    if (modeState == HIGH) {

      modePushCounter++;
      //Write clockmode to EEPROM memory
      EEPROM.write(ClockmodeAddr, modePushCounter);


    } 

    lastmodeState = modeState;
  }

  if (modePushCounter > 11) {
    modePushCounter=1;
    //Write clockmode to EEPROM memory
    EEPROM.write(ClockmodeAddr, modePushCounter);
  }


  hour = RTC.get(DS1307_HR,true);
  minute = RTC.get(DS1307_MIN,false);
  second = RTC.get(DS1307_SEC,false);

  DisplayHour = map(hour %12 , 0, 12, divisions-1, 0) - map(minute, 0, 59, 0, 21.3333333333333);
  DisplayMinute = map(minute, 0, 59, divisions-1, 0);
  //256 / seconds = 4.25
  DisplaySecond = 255 - (RTC.get(DS1307_SEC,false)*4.25);



  switch (modePushCounter) {
  case 1:
    clock1();

    break;

  case 2:
    clock2();
    break;

  case 3:
    clock3();
    break;

  case 4:
    clock4();
    break;

  case 5:
    clock5();
    break;

  case 6:
    clock6();
    break;

  case 7:

    clock7();
    break;

  case 8:

    clock8();
    break;

  case 9:

    clock9();
    break;
    
    case 10:

    clock10();
    break;
    
    case 11:
     clock11();
    break;

  
  default: 
    clock1();
  }

  setTime();

}


/**
 ** Interrupts
 **
 ** The interrupts provide the accurate timing needed for this project.
 ** 
 **/

// This interrupt is called on every pulse of the sensor, which represents
// one full rotation of the platter.  It is responsible for timing the
// revolution, and for initiating the first draw instance.
ISR(INT0_vect)
{



  // Capture the 16bit count on timer1, this represents one revolution
  period = TCNT1;

 
  // Reset timer1 so it can clock the next revolution
  TCNT1 = 0;
  // Reset timer0 so it can start to accurately paint the slot 
  TCNT0 = 0;
  // If there is a page flip request, flip to the next page

  flip_to_next_page();
  // Write out the first slice to the LEDs to PORTD
  PORTD = FrameBuffer[page_visible][0];
  // Divide the time of single platter rotation by the number of drawable
  // divisions

  OCR0A = (period / divisions);


  // Set our current slice to 1, since we just drew slice 0
  current_slice = 1; 

  return;




}



// This interrupt is called every time timer0 counts up to the 8bit value
// stored in the register "ORC0A", which is configured in INT0 interrupt.
// It is responsible for drawing out all the slices of the frame buffer
// during the exact moment the slot is in its proper rotational position.
// Using a 7200RPM drive with 255 divisions, this interrupt is called 
// 31,200 times a second.
ISR(TIMER0_COMPA_vect) {



  // Write out the LED value to the Frame Buffer
  PORTD = FrameBuffer[page_visible][current_slice];
  // Increment current slice, making sure to wrap it if it
  // has accidently gotten too large
  current_slice = ((current_slice + 1) % divisions);





}

// If the platter spin time overflows timer1, this is called
ISR(TIMER1_OVF_vect) {
}









